From e9885f9cdee2121f927f3b5b0c5603cf148acd3b Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 30 Sep 2020 11:40:37 +0200 Subject: [PATCH] gtk-demo: Make gltransitions demo a bit snazzier This adds a bunch of snazz to the gltransitions demo. It is perhaps a bit overloaded now, but it demos everything that we can do. Changes: * The fire shader is now not a bin, it just renders an animating background with no textures involved. * The stacks don't all start on the same page. * The shaderbin passes the mouse coordinate to the shader. * The shaderbin allows specifying a "border" so that you can cause effects outside the bin child (something that is new to gtk4). * All the buttons and the stacks are now in shader-bins that runs a wobbly-widget effect based on the mouse position that wobbles outside the child allocation. --- demos/gtk-demo/demo.gresource.xml | 3 ++- demos/gtk-demo/fire.glsl | 4 +-- demos/gtk-demo/gltransition.c | 44 ++++++++++++++++++++++--------- demos/gtk-demo/gtkshaderbin.c | 27 +++++++++++++++++-- demos/gtk-demo/gtkshaderbin.h | 3 ++- demos/gtk-demo/gtkshaderstack.c | 9 +++++++ demos/gtk-demo/gtkshaderstack.h | 4 ++- demos/gtk-demo/ripple.glsl | 43 ++++++++++++++++++++++++++++++ 8 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 demos/gtk-demo/ripple.glsl diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index cc9727481c..d279d08af5 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -138,8 +138,9 @@ gtkshaderstack.h gtkshaderbin.h gtkshaderbin.c + ripple.glsl fire.glsl - transition1.glsl + transition1.glsl transition2.glsl transition3.glsl transition4.glsl diff --git a/demos/gtk-demo/fire.glsl b/demos/gtk-demo/fire.glsl index e62fe1bd39..1992688206 100644 --- a/demos/gtk-demo/fire.glsl +++ b/demos/gtk-demo/fire.glsl @@ -1,5 +1,4 @@ uniform float u_time; -uniform sampler2D u_texture1; /* 2D -> [0..1] random number generator */ float random(vec2 st) { @@ -67,6 +66,5 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec vec3 color = 1.0 / (w*w/c + 1.0); // Mix in widget - vec4 widget = GskTexture(u_texture1,uv); - fragColor = gsk_premultiply(mix(vec4(color,1), widget, 1.0-color.x)); + fragColor = gsk_premultiply(vec4(color, color.x)); } diff --git a/demos/gtk-demo/gltransition.c b/demos/gtk-demo/gltransition.c index 24b6f96f30..8106e4ea89 100644 --- a/demos/gtk-demo/gltransition.c +++ b/demos/gtk-demo/gltransition.c @@ -85,15 +85,15 @@ clicked_cb (GtkGestureClick *gesture, } static GtkWidget * -fire_bin_new (void) +ripple_bin_new (void) { GtkWidget *bin = gtk_shader_bin_new (); static GskGLShader *shader = NULL; if (shader == NULL) - shader = gsk_gl_shader_new_from_resource ("/gltransition/fire.glsl"); + shader = gsk_gl_shader_new_from_resource ("/gltransition/ripple.glsl"); - gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT); + gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT, 20); return bin; } @@ -131,6 +131,7 @@ update_paintable (GtkWidget *widget, static GtkWidget * make_shader_stack (const char *name, const char *resource_path, + int active_child, GtkWidget *scale) { GtkWidget *stack, *child, *widget, *vbox, *hbox, *bin; @@ -219,6 +220,8 @@ make_shader_stack (const char *name, gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child); + gtk_shader_stack_set_active (GTK_SHADER_STACK (stack), active_child); + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); widget = gtk_center_box_new (); @@ -245,7 +248,10 @@ make_shader_stack (const char *name, gtk_box_append (GTK_BOX (vbox), widget); - gtk_box_append (GTK_BOX (vbox), stack); + GtkWidget *bin2 = ripple_bin_new (); + gtk_shader_bin_set_child (GTK_SHADER_BIN (bin2), stack); + + gtk_box_append (GTK_BOX (vbox), bin2); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER); @@ -254,13 +260,13 @@ make_shader_stack (const char *name, button = gtk_button_new_from_icon_name ("go-previous"); g_signal_connect (button, "clicked", G_CALLBACK (go_back), stack); - bin = fire_bin_new (); + bin = ripple_bin_new (); gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button); gtk_box_append (GTK_BOX (hbox), bin); button = gtk_button_new_from_icon_name ("go-next"); g_signal_connect (button, "clicked", G_CALLBACK (go_forward), stack); - bin = fire_bin_new (); + bin = ripple_bin_new (); gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button); gtk_box_append (GTK_BOX (hbox), bin); @@ -270,7 +276,8 @@ make_shader_stack (const char *name, static GtkWidget * create_gltransition_window (GtkWidget *do_widget) { - GtkWidget *window, *headerbar, *scale, *grid; + GtkWidget *window, *headerbar, *scale, *outer_grid, *grid, *background; + GdkPaintable *paintable; window = gtk_window_new (); gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget)); @@ -284,8 +291,21 @@ create_gltransition_window (GtkWidget *do_widget) gtk_window_set_default_size (GTK_WINDOW (window), 800, 600); g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL); + outer_grid = gtk_grid_new (); + gtk_window_set_child (GTK_WINDOW (window), outer_grid); + + paintable = gsk_shader_paintable_new (gsk_gl_shader_new_from_resource ("/gltransition/fire.glsl"), NULL); + background = gtk_picture_new_for_paintable (paintable); + gtk_widget_add_tick_callback (background, update_paintable, NULL, NULL); + + gtk_grid_attach (GTK_GRID (outer_grid), + background, + 0, 0, 1, 1); + grid = gtk_grid_new (); - gtk_window_set_child (GTK_WINDOW (window), grid); + gtk_grid_attach (GTK_GRID (outer_grid), + grid, + 0, 0, 1, 1); gtk_widget_set_halign (grid, GTK_ALIGN_CENTER); gtk_widget_set_valign (grid, GTK_ALIGN_CENTER); @@ -299,16 +319,16 @@ create_gltransition_window (GtkWidget *do_widget) gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale), + make_shader_stack ("Wind", "/gltransition/transition1.glsl", 0, scale), 0, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale), + make_shader_stack ("Radial", "/gltransition/transition2.glsl", 1, scale), 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale), + make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", 2, scale), 0, 1, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale), + make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", 3, scale), 1, 1, 1, 1); return window; diff --git a/demos/gtk-demo/gtkshaderbin.c b/demos/gtk-demo/gtkshaderbin.c index 3a97311c3e..9148e292a3 100644 --- a/demos/gtk-demo/gtkshaderbin.c +++ b/demos/gtk-demo/gtkshaderbin.c @@ -4,6 +4,7 @@ typedef struct { GskGLShader *shader; GtkStateFlags state; GtkStateFlags state_mask; + float extra_border; gboolean compiled; gboolean compiled_ok; } ShaderInfo; @@ -16,6 +17,7 @@ struct _GtkShaderBin GPtrArray *shaders; guint tick_id; float time; + float mouse_x, mouse_y; gint64 first_frame_time; }; @@ -71,10 +73,25 @@ gtk_shader_bin_tick (GtkWidget *widget, return G_SOURCE_CONTINUE; } +static void +motion_cb (GtkEventControllerMotion *controller, + double x, + double y, + GtkShaderBin *self) +{ + self->mouse_x = x; + self->mouse_y = y; +} + static void gtk_shader_bin_init (GtkShaderBin *self) { + GtkEventController *controller; self->shaders = g_ptr_array_new_with_free_func ((GDestroyNotify)shader_info_free); + + controller = gtk_event_controller_motion_new (); + g_signal_connect (controller, "motion", G_CALLBACK (motion_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), controller); } void @@ -132,12 +149,14 @@ void gtk_shader_bin_add_shader (GtkShaderBin *self, GskGLShader *shader, GtkStateFlags state, - GtkStateFlags state_mask) + GtkStateFlags state_mask, + float extra_border) { ShaderInfo *info = g_new0 (ShaderInfo, 1); info->shader = g_object_ref (shader); info->state = state; info->state_mask = state_mask; + info->extra_border = extra_border; g_ptr_array_add (self->shaders, info); @@ -198,10 +217,14 @@ gtk_shader_bin_snapshot (GtkWidget *widget, if (self->active_shader->compiled_ok) { + float border = self->active_shader->extra_border; + graphene_vec2_t mouse; + graphene_vec2_init (&mouse, self->mouse_x + border, self->mouse_y + border); gtk_snapshot_push_gl_shader (snapshot, self->active_shader->shader, - &GRAPHENE_RECT_INIT(0, 0, width, height), + &GRAPHENE_RECT_INIT(-border, -border, width+2*border, height+2*border), gsk_gl_shader_format_args (self->active_shader->shader, "u_time", self->time, + "u_mouse", &mouse, NULL)); gtk_widget_snapshot_child (widget, self->child, snapshot); gtk_snapshot_gl_shader_pop_texture (snapshot); diff --git a/demos/gtk-demo/gtkshaderbin.h b/demos/gtk-demo/gtkshaderbin.h index eabac8f0d6..32ed18fe6f 100644 --- a/demos/gtk-demo/gtkshaderbin.h +++ b/demos/gtk-demo/gtkshaderbin.h @@ -12,7 +12,8 @@ GtkWidget *gtk_shader_bin_new (void); void gtk_shader_bin_add_shader (GtkShaderBin *self, GskGLShader *shader, GtkStateFlags state, - GtkStateFlags state_mask); + GtkStateFlags state_mask, + float extra_border); void gtk_shader_bin_set_child (GtkShaderBin *self, GtkWidget *child); GtkWidget *gtk_shader_bin_get_child (GtkShaderBin *self); diff --git a/demos/gtk-demo/gtkshaderstack.c b/demos/gtk-demo/gtkshaderstack.c index 27194df3b1..9b71c22855 100644 --- a/demos/gtk-demo/gtkshaderstack.c +++ b/demos/gtk-demo/gtkshaderstack.c @@ -350,3 +350,12 @@ gtk_shader_stack_add_child (GtkShaderStack *self, else gtk_widget_set_child_visible (child, FALSE); } + +void +gtk_shader_stack_set_active (GtkShaderStack *self, + int index) +{ + stop_transition (self); + self->current = MIN (index, self->children->len); + update_child_visible (self); +} diff --git a/demos/gtk-demo/gtkshaderstack.h b/demos/gtk-demo/gtkshaderstack.h index 9f8e4e45e5..16d1fe9e3f 100644 --- a/demos/gtk-demo/gtkshaderstack.h +++ b/demos/gtk-demo/gtkshaderstack.h @@ -14,7 +14,9 @@ void gtk_shader_stack_set_shader (GtkShaderStack *self, void gtk_shader_stack_add_child (GtkShaderStack *self, GtkWidget *child); void gtk_shader_stack_transition (GtkShaderStack *self, - gboolean forward); + gboolean forward); +void gtk_shader_stack_set_active (GtkShaderStack *self, + int index); G_END_DECLS diff --git a/demos/gtk-demo/ripple.glsl b/demos/gtk-demo/ripple.glsl new file mode 100644 index 0000000000..028c14c159 --- /dev/null +++ b/demos/gtk-demo/ripple.glsl @@ -0,0 +1,43 @@ +uniform float u_time; +uniform vec2 u_mouse; +uniform sampler2D u_texture1; + +#define PI 3.141592654 + +float decay(float v, float t) +{ + return v * (1.0 / (1.0 + t*t)); +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) +{ + // Temporary to loop time every 1 sec + float time = u_time; + // we normalize all the effects according to the min height/width + float size = min(resolution.x, resolution.y); + + // Animate one wave over size in 0.3 sec + float wave_speed = size / 0.3; + float wave_length = size / 1.0; + float wave_height = size * 0.1; + + vec2 center = u_mouse; + vec2 direction_from_center = fragCoord - center; + float distance_from_center = length(direction_from_center); + + /* Normalize direction */ + direction_from_center = direction_from_center / distance_from_center; + + float propagation_length = time * wave_speed; + + float t = (propagation_length - distance_from_center) / wave_length; + float offset_magnitude = 0; + if (t > 0.0) + offset_magnitude = decay(wave_height * sin(t * 2.0 * PI), t); + + vec2 offset = direction_from_center * min(offset_magnitude, distance_from_center); + vec2 source = fragCoord - offset; + + vec2 uv2 = source / resolution; + fragColor = GskTexture(u_texture1, vec2(uv2.x, 1.0-uv2.y)); +} -- 2.30.2